/*****************************************************************************
 * Copyright (C) Stacy Curl. All rights reserved.                            *
 * ------------------------------------------------------------------------- *
 * The software in this package is published under the terms of the BSD      *
 * style license a copy of which has been included with this distribution in *
 * the LICENSE.txt file.                                                     *
 *****************************************************************************/
package picounit.verify;

import picounit.DelegateVerify;
import picounit.Should;
import picounit.Verify;
import picounit.features.delegate_verify.DefaultDelegateVerify;
import picounit.features.mocking.MockInvocationInspector;
import picounit.mocker.BooleanConsequenceMatcher;
import picounit.mocker.ByteConsequenceMatcher;
import picounit.mocker.CharConsequenceMatcher;
import picounit.mocker.ConsequenceMatcher;
import picounit.mocker.DoubleConsequenceMatcher;
import picounit.mocker.FloatConsequenceMatcher;
import picounit.mocker.IntConsequenceMatcher;
import picounit.mocker.LongConsequenceMatcher;
import picounit.mocker.MockControl;
import picounit.mocker.MockFactory;
import picounit.mocker.ShortConsequenceMatcher;
import picounit.mocker.StringConsequenceMatcher;
import picounit.util.Equal;
import picounit.verify.constraint.EqualUtil;
import previous.picounit.Test;
import previous.picounit.TestCase;

public class DelegateVerifyTest extends TestCase implements Test {
	private final previous.picounit.Should should;

	private final Should mockShould = injectCollaborator();
	private final Verify mockVerify = injectCollaborator();

	private final MockInvocationInspector mockInvocationInspector = injectCollaborator();
	private final CustomType customType = injectCollaborator();

	private final BooleanConsequenceMatcher booleanConsequenceMatcher = injectCollaborator();
	private final ByteConsequenceMatcher byteConsequenceMatcher = injectCollaborator();
	private final CharConsequenceMatcher charConsequenceMatcher = injectCollaborator();
	private final DoubleConsequenceMatcher doubleConsequenceMatcher = injectCollaborator();
	private final FloatConsequenceMatcher floatConsequenceMatcher = injectCollaborator();
	private final IntConsequenceMatcher intConsequenceMatcher = injectCollaborator();
	private final LongConsequenceMatcher longConsequenceMatcher = injectCollaborator();
	private final ShortConsequenceMatcher shortConsequenceMatcher = injectCollaborator();
	private final StringConsequenceMatcher stringConsequenceMatcher = injectCollaborator();
	private final ConsequenceMatcher consequenceMatcher = injectCollaborator();
	private final ByteConstraints byteConstraints = injectCollaborator();
	private final CharacterConstraints characterConstraints = injectCollaborator();
	private final DoubleConstraints doubleConstraints = injectCollaborator();
	private final FloatConstraints floatConstraints = injectCollaborator();
	private final IntegerConstraints integerConstraints = injectCollaborator();
	private final LongConstraints longConstraints = injectCollaborator();
	private final ShortConstraints shortConstraints = injectCollaborator();
	private final ExtendedStringConstraints stringConstraints = injectCollaborator();
	private final TypedConstraints typedConstraints = injectCollaborator();
	private final BooleanConstraints booleanConstraints = injectCollaborator();
	private final MockFactory mockFactory = injectCollaborator();
	private final MockControl mockControl = injectCollaborator();

	private final DelegateVerify delegateVerify = new DefaultDelegateVerify(mockShould, mockControl,
		mockFactory, mockInvocationInspector, mockVerify);

	public static class CustomType implements Equal<CustomType>{
		private final String value;

		public CustomType(String value) {
			this.value = value;
		}
		
		public boolean someMethod() {
			return true;
		}
		
		public boolean hasValue(String value) {
			return this.value.equals(value);
		}

		public boolean equalTo(CustomType other) {
			return other.hasValue(value);
		}
		
		@Override
		public String toString() {
			return "CustomType: " + value;
		}
		
		@Override
		public boolean equals(Object object) {
			return EqualUtil.isEqual(this, object);
		}
		
		@Override
		public int hashCode() {
			return value.hashCode();
		}
	}

	public DelegateVerifyTest(previous.picounit.Should should) {
		this.should = should;
	}

	public void testTestsActualInvocationReturnsSameBooleanValueAsDelegateToInvocation() {
		boolean actualValue = false;

		should.call(mockShould.call(false))
			.andReturn(booleanConsequenceMatcher);

		shouldCall:
			booleanConsequenceMatcher.andReturn(DefaultDelegateVerify.BOOLEAN_DELEGATE_RETURN.booleanValue());
			mockShould.expectAboveWhenTheFollowingOccurs();
			
		should.call(mockVerify.that(actualValue))
			.andReturn(booleanConstraints);
		
		shouldCall:
			booleanConstraints.isEqualTo(DefaultDelegateVerify.BOOLEAN_DELEGATE_RETURN.booleanValue());
			mockControl.verify();

		should.expectAboveWhenTheFollowingOccurs();

		delegateVerify.delegateTo(false).whenCalling(actualValue);
	}

	public void testTestsActualInvocationReturnsSameByteValueAsDelegateToInvocation() {
		byte ignored = (byte) 0xBB;
		byte actualValue = (byte) 0xAA;

		should.call(mockShould.call(ignored))
			.andReturn(byteConsequenceMatcher);
		
		shouldCall:
			byteConsequenceMatcher.andReturn(DefaultDelegateVerify.BYTE_DELEGATE_RETURN.byteValue());
			mockShould.expectAboveWhenTheFollowingOccurs();
			
		should.call(mockVerify.that(actualValue))
			.andReturn(byteConstraints);
		
		shouldCall:
			byteConstraints.isEqualTo(DefaultDelegateVerify.BYTE_DELEGATE_RETURN.byteValue());
			mockControl.verify();

		should.expectAboveWhenTheFollowingOccurs();

		delegateVerify.delegateTo(ignored).whenCalling(actualValue);
	}

	public void testTestsActualInvocationReturnsSameCharValueAsDelegateToInvocation() {
		char ignored = 'i';
		char actualValue = 'a';

		should.call(mockShould.call(ignored))
			.andReturn(charConsequenceMatcher);
		
		shouldCall:
			charConsequenceMatcher.andReturn(DefaultDelegateVerify.CHAR_DELEGATE_RETURN.charValue());
			mockShould.expectAboveWhenTheFollowingOccurs();
		
		should.call(mockVerify.that(actualValue))
			.andReturn(characterConstraints);
		
		shouldCall:
			characterConstraints.isEqualTo(DefaultDelegateVerify.CHAR_DELEGATE_RETURN.charValue());
			mockControl.verify();

		should.expectAboveWhenTheFollowingOccurs();

		delegateVerify.delegateTo(ignored).whenCalling(actualValue);
	}
	
	public void testTestsActualInvocationReturnsSameDoubleValueAsDelegateToInvocation() {
		double ignored = 123.456f;
		double actualValue = 456.123f;

		should.call(mockShould.call(ignored))
			.andReturn(doubleConsequenceMatcher);
		
		shouldCall:
			doubleConsequenceMatcher.andReturn(DefaultDelegateVerify.DOUBLE_DELEGATE_RETURN.doubleValue());
			mockShould.expectAboveWhenTheFollowingOccurs();
			
		should.call(mockVerify.that(actualValue))
			.andReturn(doubleConstraints);
		
		shouldCall:
			doubleConstraints.isEqualTo(DefaultDelegateVerify.DOUBLE_DELEGATE_RETURN.doubleValue());
			mockControl.verify();

		should.expectAboveWhenTheFollowingOccurs();

		delegateVerify.delegateTo(ignored).whenCalling(actualValue);
	}

	public void testTestsActualInvocationReturnsSameFloatValueAsDelegateToInvocation() {
		float ignored = 123.456f;
		float actualValue = 456.123f;

		should.call(mockShould.call(ignored))
			.andReturn(floatConsequenceMatcher);
		
		shouldCall:
			floatConsequenceMatcher.andReturn(DefaultDelegateVerify.FLOAT_DELEGATE_RETURN.floatValue());
			mockShould.expectAboveWhenTheFollowingOccurs();
		
		should.call(mockVerify.that(actualValue))
			.andReturn(floatConstraints);
		
		shouldCall:
			floatConstraints.isEqualTo(DefaultDelegateVerify.FLOAT_DELEGATE_RETURN.floatValue());
			mockControl.verify();

		should.expectAboveWhenTheFollowingOccurs();

		delegateVerify.delegateTo(ignored).whenCalling(actualValue);
	}

	public void testTestsActualInvocationReturnsSameIntValueAsDelegateToInvocation() {
		int ignored = 123;
		int actualValue = 456;

		should.call(mockShould.call(ignored))
			.andReturn(intConsequenceMatcher);
		
		shouldCall:
			intConsequenceMatcher.andReturn(DefaultDelegateVerify.INT_DELEGATE_RETURN.intValue());
			mockShould.expectAboveWhenTheFollowingOccurs();
			
		should.call(mockVerify.that(actualValue))
			.andReturn(integerConstraints);
		
		shouldCall:
			integerConstraints.isEqualTo(DefaultDelegateVerify.INT_DELEGATE_RETURN.intValue());
			mockControl.verify();

		should.expectAboveWhenTheFollowingOccurs();

		delegateVerify.delegateTo(ignored).whenCalling(actualValue);
	}

	public void testTestsActualInvocationReturnsSameLongValueAsDelegateToInvocation() {
		long ignored = 123L;
		long actualValue = 456L;
		
		should.call(mockShould.call(ignored))
			.andReturn(longConsequenceMatcher);
		
		shouldCall:
			longConsequenceMatcher.andReturn(DefaultDelegateVerify.LONG_DELEGATE_RETURN.longValue());
			mockShould.expectAboveWhenTheFollowingOccurs();
		
		should.call(mockVerify.that(actualValue))
			.andReturn(longConstraints);

		shouldCall:
			longConstraints.isEqualTo(DefaultDelegateVerify.LONG_DELEGATE_RETURN.longValue());
			mockControl.verify();

		should.expectAboveWhenTheFollowingOccurs();

		delegateVerify.delegateTo(ignored).whenCalling(actualValue);
	}

	public void testTestsActualInvocationReturnsSameShortValueAsDelegateToInvocation() {
		short ignored = 123;
		short actualValue = 456;

		should.call(mockShould.call(ignored))
			.andReturn(shortConsequenceMatcher);
		
		shouldCall:
			shortConsequenceMatcher.andReturn(DefaultDelegateVerify.SHORT_DELEGATE_RETURN.shortValue());
			mockShould.expectAboveWhenTheFollowingOccurs();
			
		should.call(mockVerify.that(actualValue))
			.andReturn(shortConstraints);
		
		shouldCall:
			shortConstraints.isEqualTo(DefaultDelegateVerify.SHORT_DELEGATE_RETURN.shortValue());
			mockControl.verify();

		should.expectAboveWhenTheFollowingOccurs();

		delegateVerify.delegateTo(ignored).whenCalling(actualValue);
	}

	@SuppressWarnings("unchecked")
	public void testTestsActualInvocationReturnsSameObjectValueAsDelegateToInvocation() {
		Object ignored = new Object();
		Object actualValue = new Object();

		should.call(mockInvocationInspector.getLastInvocationReturnType())
			.andReturn(Object.class);
		
		should.call(mockShould.call(ignored))
			.andReturn(consequenceMatcher);

		shouldCall:
			consequenceMatcher.andReturn(DefaultDelegateVerify.OBJECT_DELEGATE_RETURN);
			mockShould.expectAboveWhenTheFollowingOccurs();
			
		should.call(mockVerify.that(actualValue))
			.andReturn(typedConstraints);
		
		shouldCall:
			typedConstraints.isEqualTo(DefaultDelegateVerify.OBJECT_DELEGATE_RETURN);
			mockControl.verify();

		should.expectAboveWhenTheFollowingOccurs();

		delegateVerify.delegateTo(ignored).whenCalling(actualValue);
	}
	
	public void testTestsActualInvocationReturnsSameStringValueAsDelegateToInvocation() {
		String ignored = "ignored";
		String actualValue = "actual";

		should.call(mockShould.call(ignored))
			.andReturn(stringConsequenceMatcher);
		
		shouldCall:
			stringConsequenceMatcher.andReturn(DefaultDelegateVerify.STRING_DELEGATE_RETURN);
			mockShould.expectAboveWhenTheFollowingOccurs();
		
		should.call(mockVerify.that(actualValue))
			.andReturn(stringConstraints);

		shouldCall:
			stringConstraints.isEqualTo(DefaultDelegateVerify.STRING_DELEGATE_RETURN);
			mockControl.verify();

		should.expectAboveWhenTheFollowingOccurs();

		delegateVerify.delegateTo(ignored).whenCalling(actualValue);
	}

	@SuppressWarnings("unchecked")
	public void testTestsActualInvocationReturnsSameCustomTypeValueAsDelegateToInvocation() {
		CustomType ignored = new CustomType("ignored");
		CustomType actualValue = new CustomType("ignored");

		should.call(mockInvocationInspector.getLastInvocationReturnType())
			.andReturn(ignored.getClass());

		should.call(mockFactory.mock(CustomType.class))
			.andReturn(customType);

		should.call(mockShould.call(ignored))
			.andReturn(consequenceMatcher);

		shouldCall:
			consequenceMatcher.andReturn(customType);
			mockShould.expectAboveWhenTheFollowingOccurs();

		should.call(mockVerify.that(actualValue))
			.andReturn(typedConstraints);
		
		shouldCall:
			typedConstraints.isEqualTo(customType);
			mockControl.verify();
		
		should.expectAboveWhenTheFollowingOccurs();

		delegateVerify.delegateTo(ignored).whenCalling(actualValue);
	}
}
